介紹
Redux 是一個 Global State Management ,提供了一個 Store 的使用所有的State(reducer)都會在這裡 ,而當有些時候需要發Request,前面的Redux篇有介紹使用最基礎的 Thunk 來發Fetch 請求(通常都會稱為 Side Effect), 而Redux-Saga 提供各種 effect 的method 在管理這些 Side Effect的工作
Redux-Saga 的Store設定
以下設定跟 Redux 差不多,這邊的rootSaga 要給設定一個起始路徑,再使用 createSagaMiddleware 建立一個 sagaMiddleware instance 除了在 Reudx 中applyMiddleware ,要再額外指定 saga 的工作路徑 store.sagaTask = sagaMiddleware.run(rootSaga)
import {createStore, applyMiddleware} from 'redux'
import {composeWithDevTools} from 'redux-devtools-extension'
import withRedux from 'next-redux-wrapper'
import nextReduxSaga from 'next-redux-saga'
import createSagaMiddleware from 'redux-saga'
import rootReducer, {exampleInitialState} from './reducer'
import rootSaga from './saga'
const sagaMiddleware = createSagaMiddleware()
export function configureStore (initialState = exampleInitialState) {
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(sagaMiddleware))
)
store.sagaTask = sagaMiddleware.run(rootSaga)
return store
}
export function withReduxSaga (BaseComponent) {
return withRedux(configureStore)(nextReduxSaga(BaseComponent))
}
在Redux-Saga中 提供了許多effects, 這些effect take類的是監聽, put是發dispatch,call是發 request 以下方的程式碼來介紹
import {all, call, put, take, takeLatest} from 'redux-saga/effects'
每一個 effect都有各自的功能,這邊介紹一些簡單的,還有其他更多effect可以參考 Redux-Saga 官方
all : 可以想像 while迴圈 包住裡面的各種 effect 邏輯,通常根部會有fork支線比較清楚,但也可以沒有
call : 就是 發一個 dispatch payload, 所以在原本的寫在 actions.js 的dispatch就會移到這邊的call來發送 paylad 然後 這邊的takeLatest則監聽發送 actionTypes 這邊保持原本的 actions.js 發 actiontype 但處理的地方以下方為例則改到 loadDataSaga處理
take : 監聽如果有對應的 action.type 就看要執行哪個 method
takeLatest : 如果多發很多筆 action.type 只取最後一個
function * rootSaga () {
yield all([
call(runClockSaga),
takeLatest(actionTypes.LOAD_DATA, loadDataSaga)
])
}
在下面可以看到 put ,這裡的 put 就會發dispatch ,而可以在Saga之中常看見大量使用 yield 而且 function 都會加上*這裡是使用 Es6 generator ,一般可以理解成迴圈中執行某些 Event 完就到外層看看有沒有要在做什麼,如果沒有的話就回到原來的迴圈 繼續做下一步 ,也因此有這個特性所以可以在 Redux-Saga 中做到取消發 fetch 這件事情
yield put(tickClock(false))
Next.js中要注意的是 import 'isomorphic-unfetch' 這邊使用了後端也可以發fetch功能的套件,還有一個es6promise的polyfill 補丁 ,其他部分跟SPA使用一樣
/* global fetch */
import {delay} from 'redux-saga'
import {all, call, put, take, takeLatest} from 'redux-saga/effects'
import es6promise from 'es6-promise'
import 'isomorphic-unfetch'
import {actionTypes, failure, loadDataSuccess, tickClock} from './actions'
es6promise.polyfill()
function * runClockSaga () {
yield take(actionTypes.START_CLOCK)
while (true) {
yield put(tickClock(false))
yield call(delay, 800)
}
}
function * loadDataSaga () {
try {
const res = yield fetch('https://jsonplaceholder.typicode.com/users')
const data = yield res.json()
yield put(loadDataSuccess(data))
} catch (err) {
yield put(failure(err))
}
}
function * rootSaga () {
yield all([
call(runClockSaga),
takeLatest(actionTypes.LOAD_DATA, loadDataSaga)
])
}
export default rootSaga
總結
Redux-Saga 使用了許多 effect 來的做 side effect 的管理,比起thunk 因為有 es6 generator 的特性,增加許多彈性,設定上一般會把原來的 action.js 中發 dispatch的部分換到 Redex-Saga 中的 put去發 ,在 Next.js 設定部分也是要注意到SSR補丁的部分
github
https://github.com/zeit/next.js/tree/canary/examples/with-redux-saga